Das Euler-Verfahren#

Das Euler-Verfahren ist das einfachste numerische Verfahren zur Lösung eines Anfangswertproblems. Zum Verständnis benötigen wir Vorkenntnisse über

  • Differentialgleichungen 1. Ordnung,

  • Taylor-Polynom Grad 1 und

  • Python-Kenntnisse (Pandas, for-Schleife, Plotly).

Lernziele#

Lernziele

  • Sie können eine Bewegung als Anfangswertproblem formulieren.

  • Sie können das Euler-Verfahren zur numerischen Lösung eines Anfangswertproblems in Python implementieren.

  • Sie können mit der Tangente bzw. dem Taylorpolynom 1. Grades begründen, warum das Euler-Verfahren funktioniert.

  • Sie wissen, welchen Einfluss die Schrittweite auf den Fehler des Euler-Verfahrens hat.

Geschwindigkeit aus der Beschleunigung rekonstruieren#

Wir führen das folgende Experiment aus. Eine Person setzt sich auf ein Bobby Car und fährt eine kurze Zeit, so schnell sie kann.

_images/bobby_car_symbolbild.jpg

Fig. 1 Symbolbild der Bobby-Car-Fahrt (Quelle: Freepik, Lizenz: Freepik Premium Lizenz)#

Zu Beginn der Bobby-Car-Fahrt ist die Geschwindigkeit Null. Wir bezeichnen die Geschwindigkeit mit \(v\) [m/s] und die Zeit mit \(t\) [s]. Es gilt also für den Start \(t_0 = 0 \,\mathrm{s}\)

\[v(0) = 0 \frac{\mathrm{m}}{\mathrm{s}}.\]

Die Beschleunigung messen wir mit Hilfe eines Smartphones und der App Phyphox. Beispielhaft erhalten wir die folgenden Beschleunigungsmessungen, die wir mit dem Python-Modul Pandas importieren.

import pandas as pd

daten = pd.read_csv('data/Beschleunigung_V01.csv')
daten.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 603 entries, 0 to 602
Data columns (total 5 columns):
 #   Column                         Non-Null Count  Dtype  
---  ------                         --------------  -----  
 0   Time (s)                       603 non-null    float64
 1   Linear Acceleration x (m/s^2)  603 non-null    float64
 2   Linear Acceleration y (m/s^2)  603 non-null    float64
 3   Linear Acceleration z (m/s^2)  603 non-null    float64
 4   Absolute acceleration (m/s^2)  603 non-null    float64
dtypes: float64(5)
memory usage: 23.7 KB

Wir verwenden das Python-Modul Plotly Express, um die Beschleunigungsdaten in x-Richtung abhängig von der Zeit zu visualisieren. Die x-Richtung wird gewählt, weil das Smartphone beim Experiment entsprechend montiert war (siehe Koordinatensystem von Phyphox).

import plotly.express as px

fig = px.scatter(daten, x = 'Time (s)', y = 'Linear Acceleration x (m/s^2)')
fig.show()

Damit wir einfacher auf die Messdaten zugreifen können, speichern wir die Zeitmessungen Time (s) in der Variablen t und die Beschleunigung Linear Acceleration x (m/s^2) in der Variablen a.

t = daten['Time (s)']
a = daten['Linear Acceleration x (m/s^2)']

Nun wollen wir numerisch die Geschwindigkeiten aus den Beschleunigungsdaten rekonstruieren. Generell führt eine konstante Beschleunigung \(a\), die eine Zeitdauer \(\Delta t\) lang wirkt, zu einer Geschwindigkeitsänderung \(\Delta v\):

\[\Delta v = a \cdot \Delta t.\]

Da wir nur diskrete Messwerte vorliegen haben, nehmen wir an, dass die Beschleunigung \(a_0\) konstant im Zeitintervall \([t_0, t_1]\) gewirkt hat und können damit nun die neue Geschwindigkeit \(v_1\) berechnen. In mathematischen Formeln erhalten wir

\[v_1 - v_0 = a_0 \cdot (t_1 - t_0)\quad \Rightarrow \quad v_1 = v_0 + a_0 \cdot (t_1 - t_0).\]

In Python ergibt sich mit dem Anfangswert v0 = 0

# Anfangswert
t0 = 0
v0 = 0

# neue Geschwindigkeit
v1 = v0 + a[0] * (t[1] - t0)
print(f'v1 = {v1}')
v1 = 0.0

Jetzt, wo wir \(v_1\) berechnet haben, können wir erneut annehmen, dass \(a_1\) konstant im Zeitintervall \([t_1, t_2]\) gewirkt hat und erhalten:

\[v_2 - v_1 = a_1 \cdot (t_2 - t_1)\quad \Rightarrow \quad v_2 = v_1 + a_1 \cdot (t_2 - t_1).\]

In Python ergibt sich

v2 = v1 + a[1] * (t[2] - t[1])
print(f'v2 = {v2}')
v2 = -0.000356769695151

Diese Prozedur können wir immer weiter fortsetzen, bis wir die Geschwindigkeit auf dem gesamten gemessenen Zeitintervall rekonstruiert haben. Das wollen wir nicht händisch, sondern automatisiert mit Python machen. Daher bestimmen wir zunächst die Anzahl der Messwerte \(N\) und initialisieren anschließend ein NumPy-Array der Länge \(N\), das die Geschwindigkeitswerte aufnehmen soll.

import numpy as np

N = len(t)
v = np.zeros(N)

Die obige Berechnung der Geschwindigkeitsänderung

\[\Delta v = a \cdot \Delta t\]

lässt sich für beliebige Zeitintervalle \([t_i, t_{i+1}]\) formulieren als

\[v_{i+1} - v_{i} = a_{i} \cdot (t_{i+1} - t_{i})\quad \Rightarrow \quad v_{i+1} = v_{i} + a_{i} \cdot (t_{i+1} - t_{i}).\]

Wird eine for-Schleife verwendet, berechnet der folgende Python-Code nacheinander die Geschwindigkeitswerte aus den diskreten Beschleunigungsmessungen:

for i in range(N-1):
    v[i+1] = v[i] + a[i] * (t[i+1] - t[i])

Nun können wir die berechneten Geschwindigkeiten visualisieren.

fig = px.line(x = t, y = v,
  labels = {'x': 'Time (s)', 'y': 'Velocity in x (m/s)'})
fig.show()

Anfangswertprobleme allgemein mit dem Euler-Verfahren lösen#

In dem obigen Beispiel haben wir aus der Messung einer Beschleunigung die Geschwindigkeit rekonstruiert. Allgemein betrachtet kann der Zusammenhang zwischen Geschwindigkeit und Beschleunigung über die Differentialgleichung

\[\dot{v}(t) = a(t)\]

beschrieben werden. Es liegt eine Differentialgleichung 1. Ordnung vor. Zusammen mit der Forderung, dass zu einem reellen Startwert \(t_0\) die Geschwindigkeit gleich der Anfangsgeschwindigkeit \(v_0\) ist, also

\[v(t_0) = v_0\]

gilt, stellt das Bobby-Car-Problem ein Anfangswertproblem dar. Wir können die oben beschriebene Prozedur auf beliebige Anfangswertprobleme übertragen.

Euler-Verfahren

Wenn das allgemeine Anfangswertproblem

\[\dot{x}(t) = f(t, x(t)), \quad x(t_0) = x_0\]

lautet, dann beginnen wir beim Anfangswert \(t_0\) und nutzen die Schrittweite \(h > 0\), um Punkte

\[t_{i} = t_0 + i\cdot h, \quad i = 0, 1, 2, \ldots\]

zu erzeugen. Diese Punkte werden als Gitter bezeichnet. Dann wird die Lösungsfunktion \(x\) durch die Werte

\[x_{i+1} = x_{i} + f(t_{i}, x_{i}) \cdot h, \quad i = 0, 1, 2, \ldots\]

angenähert. Dabei haben wir die Abkürzung \(x_{i}\) für \(x_{i} = x(t_{i})\) verwendet. Wir hangeln uns also immer ein Stückchen “h” weiter. Diese Prozedur wird Euler-Verfahren genannt.

Begründen lässt sich das Euler-Verfahren mit der Taylor-Formel bzw. der Tangentengleichung. Das Taylorpolynom vom Grad 1 an einem Entwicklungspunkt \(t_{*}\) lautet

\[T_{1, f}(t) = f(t_{*}) + f'(t_{*})\cdot (t-t_{*}).\]

Übertragen auf unser Anfangswertproblem lautet das Taylorpolynom vom Grad 1 für den Entwicklungspunkt \(t_i\)

\[T_{1,x}(t) = x(t_{i}) + \dot{x}(t_{i}) \cdot (t-t_{i}).\]

Wir gehen davon aus, dass wir in der Nähe des Entwicklungspunktes das Taylorpoluynom als Ersatz für die exakte Lösungsfunktion nehmen dürfen und setzen für die erste Ableitung \(\dot{x}\) die rechte Seite \(f(t_i, x(t_i))\) der Differentialgleichung ein.

_images/tangente.pdf

Fig. 2 Tangente bzw. Taylorpolynom 1. Grades für die Lösungsfunktion des Anfangswertproblems \(\dot{x}(t) = -0.003 x(t)^2, \; x(0)=5\) (Quelle: eigene Darstellung, Lizenz: BY-NC-SA-4.0)#

Damit erhalten wir

\[x(t) = x(t_{i}) + f(t_{i}, x(t_i)) \cdot (t-t_{i}).\]

Speziell für den nächsten Gitterpunkt \(t_{i+1}\) gilt dann näherungsweise

\[x(t_{i+1}) = x(t_{i}) + f(t_{i}, x(t_i)) \cdot (t_{i+1}-t_{i}),\]

was mit den Abkürzungen \(x_i=x(t_i)\) und \(x_{i+1} = x(t_{i+1})\) vereinfacht geschrieben werden kann als

\[x_{i+1} = x_{i} + f(t_{i}, x_i) \cdot h.\]

Wenn wir für \(t_i\) den Wert der Lösungsfunktion \(x_i\) kennen würden, könnten wir damit für den nächsten Punkt \(t_{i+1}\) den Wert \(x_{i+1}\) berechnen. Da wir ein Anfangswertproblem vorliegen haben, kennen wir \(t_0\) und \(x_0\) und können so auf \(t_1\) und \(x_1\) schließen. Und damit wiederum können wir \(t_2\) und \(x_2\) berechnen und so weiter, bis wir schließlich eine angenäherte Lösungsfunktion auf dem kompletten Intervall gefunden haben.

Das Euler-Verfahren ist das einfachste numerische Verfahren zur Lösung eines Anfangswertproblemes. Es hat aber auch Nachteile und lässt sich nicht immer stabil einsetzen. Im nächsten Abschnitt erkunden wir, welchen Einfluss die Schrittweite \(h\) auf die Qualität der angenäherten Lösung hat.

Video “Euler Verfahren” von Kochrezepte für Mathematik

Einfluss der Schrittweite#

Wie gut die numerische Lösung eine exakte Lösung eines Anfangswertproblems annähert, wird auch durch die Schrittweite bestimmt. Um den Zusammenhang zwischen der Schrittweite \(h\) und den Fehler des Euler-Verfahrens zu verstehen, betrachten wir das folgende Beispiel.

Ein Bobby-Car fährt mit einer Anfangsgeschwindigkeit von \(v_0 = 5 \mathrm{m}/\mathrm{s}\). Der Fahrer oder die Fahrerin lässt das Bobby-Car ausrollen. Dann gilt für das Abbremsen des Bobby-Cars die folgende Differentialgleichung

\[\dot{v}(t) = - k \cdot v(t)^2,\]

wobei wir annehmen, dass der Abbremsvorgang zum Zeitpunkt \(t = 0\,\mathrm{s}\) beginnt. Die Konstante \(k\) ergibt sich aus dem Luftwiderstand und hat in unserem Fall einen Wert von

\[k = 0.003 \frac{1}{\mathrm{m}}.\]

Als nächstes variieren wir die Schrittweite \(h>0\).

Lösung

Es gibt mehrere Fehlerarten. Wir betrachten hier den Gesamtfehler.

Schrittweite h

Gesamtfehler

1

0.0091

5

0.0467

10

0.0967

15

0.1511

20

0.2113

25

0.2790

30

0.3561

35

0.4568

40

0.5617

45

0.6923

50

0.8024

Vermutung: der Fehler hängt quadratisch von der Schrittweite ab.

Wir können verschiedene Arten von Fehler in Abhängigkeit von der Schrittweite untersuchen, beispielsweise

  • maximaler Fehler,

  • Gesamtfehler (RMSE) und

  • relativer Endfehler (in Prozent).

Für den maximalen Fehler wird für jeden Gitterpunkt der Abstand zwischen der angenäherten und der exakten Lösung berechnet. Der größte Abstand wird dann als maximaler Fehler bezeichnet. Beim Gesamtfehler wird die Wurzel aus dem mittleren quadratischen Fehler betrachtet, was auch als Root Mean Square Error bezeichnet wird. Beim relativen Endfehler wird nur der letzte Gitterpunkt betrachtet und der Abstand zum exakten Funktionswert in Prozent angegeben.

In dem folgenden Streudiagramm ist der Gesamtfeher (RMSE) für verschiedene Schrittweiten dargestellt. Wir vermuten, dass der Gesamtfehler von der Schrittweite quadratisch abhängt.

Diese Vermutung kann auch mathematisch mit dem Restglied für die Taylor-Formel bestätigt werden.

Zusammenfassung und Ausblick#

Das Euler-Verfahren ist ein sehr einfaches Verfahren zur numerischen Lösung eines Anfangswertproblems. Es kann verbessert werden, wenn nicht eine feste, sondern eine variable Schrittweite gewählt wird (Schrittweitensteuerung). Weitere Varianten des Euler-Verfahrens sind das implizite Euler-Verfahren und das Euler-Maruyama-Verfahren für stochastische Differentialgleichungen.